home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Shareware Grab Bag
/
Shareware Grab Bag.iso
/
015
/
hptx501.arc
/
LPTX.ASM
< prev
next >
Wrap
Assembly Source File
|
1986-06-06
|
38KB
|
1,261 lines
title LPTx : Line PrinTer Output Capture Routine
page 60,132
;------------------------------------------------------------
;
; MAIN PROGRAM Version 5.01
;
; (C) Copyright 1986 by Mark DiVecchio, All Rights Reserved
;
.xlist
; You may use and freely distribute this program for
; non-commercial applications.
;
; DISCLAMER : this program tries to perform a function which is
; not supported by DOS. It will work sometime and will not work
; other times. That kind of explains why you don't see this type
; of program on the market.
; I have tested it under DOS 2.1. Some users have
; reported trouble when running under DOS 3.x and for other users
; it works fine. USE AT YOUR OWN RISK.
;
; Mark C. DiVecchio
; 9067 Hillery Drive
; San Diego, CA 92126
; 619-566-6810
;------------------------------------------------------------
; Updates for Version 5.01 ?? ??? 86
; Still Under Development
;
; This version adds a check for DOS interuupt 21h
; function 40h for standard printer device = 0004.
;
; This addition was suggested by Dale Letterman of Seattle.
;
; Assembled using MicroSoft MASM v 4.0
;
; Program is called and used in the same way as version 3.00
;------------------------------------------------------------
; Updates for Version 5.00 13 May 86
;
; This version also takes over the DOS interuupt 21h and specifically
; checks for function 5.
; If that is the call, LPTx captures the character if LPTx has been
; activated. If it is a DOS call, LPTx assumes that DOS wants LPT1 since
; there is no way for the DOS call to specify a line printer number.
;
; Uses undocumented DOS int 21h calls 50h and 51h.
; 50h Set new current Program Segment Prefix(PSP) from
; segment number in BX
; 51h Get current PSP into BX.
; These calls are used before any file is opened by the resident portion
; of LPTx. There is some concern that DOS puts information about open files
; into the current PSP. Before we open our spooler file, we want to set
; the current PSP to our PSP and then restore it after the file I/O
; is complete. This idea was expressed in PC Magazine May 13, 1986 on page
; 314 in an article by Charles Petzold.
;
;
; This version 5.0 does not obsolete versions 4.0 and 3.0. Those versions
; may work under some conditions where this one does not and vica versa.
;------------------------------------------------------------
; Updates for Version 4.01 5 May 86
;
; Had an error in the way LPTx detected if it was already in memory.
; This error existed from back in version 3.00 and may have been
; the cause of this program locking up the system the very first
; time it was called.
;------------------------------------------------------------
; Updates for Version 4.0 25 April 86
;
; Assembled using MicroSoft MASM v 4.0
;
; Program is called and used in the same way as version 3.00
;
; Modified the code to check if DOS was running when the print interrupt
; occurs. If so the print request is routed back to the regular line
; printer. This will limit the use of this capture program to user
; programs which do their own output without going to DOS.
;
; In turn, this guarantees that we do re-enter DOS.
;
; The trick of saving the DOS stack was dropped in this version and
; I have resorted to another trick which I garnered from the
; following message found on info-ibmpc. I use method number 2.
;
; This version 4.0 does not obsolete version 3.0. That version may
; work under some conditions where this one does not and vica versa.
; This one worked fine for me using DOS 2.1 and 123 version 1.A.
; Will not work with Shift PrtSc.
;
comment *
Date: Thu, 30 Jan 86 08:47:51 est
Subject: File I/O from resident programs
To: allegra!seismo!usc-isib.arpa!info-ibmpc
Regarding opening up a file when you are terminate-and-stay-resident:
Be very careful when you attempt this. Many an FAT has been eaten for
lunch when I first tried doing it. Two ways that work like a charm:
1) Take over interrupt 0x28. This interrupt gets called by DOS
while its waiting for a key to be hit. Whenever it does get
called (your program should not be time critical, btw, as
this routine is never called from CPU intensive tasks), it
is safe to do with DOS what you will. (Except for certain
interruptions, such as Search First and Search Next, which
either you'll screw-up for the foreground task, or they'll
screw-up for you.)
2) Get the Critical Section Flag by issuing an int 21, with ah=0x34.
This returns a pointer to a flag in ES:BX. When this flag is
NULL, and interrupts are on, it is safe to play DOS games.
Unless you are the last program to take over the interrupt,
don't trust the flag word: many "fine" programs like SideKick
do not give you a true copy of the flag word on the stack, but
rather give a simple "pushf" after interrupts are turned off.
* ;end of comment
;
; More information from a message posted on USENIX:
;
comment *
From sdcsvax!ihnp4!timeinc!greenber Mon Jul 1 05:12:16 1985
Date: 30 Jun 85 17:12:37 CDT (Sun)
-----------------------------------------------------------
INT 21 - Internal - Return CritSectFlag Pointer (MSDOS generic)
REG AH = 34H
On Return:
ES:BX points to DOS "Critical Section Flag"
When byte pointed to is zero, DOS is supposed to be
safe to interrupt. NOT RELIABLE according to Chris
Dunford.
Examination of DOS 2.10 code in this area
indicates that the byte immediately FOLLOWING this
"Critical Section Flag" must be 00 to permit the
PRINT.COM interrupt to be called. This suggests that
checking the WORD pointed to, rather than the BYTE,
might increase reliability of the test greatly.
-----------------------------------------------------------
INT 28 - Internal routine for MSDOS
This interrupt is called from inside the "get input
from keyboard" routine in DOS, if and only if it is safe to use
INT 21 to access the disk at that time. It is used primarily by
the PRINT.COM routines, but any number of other routines could
be chained to it by saving the original vector, and calling it
with a FAR call (or just JMPing to it) at the end of the new
routine.
Until PRINT.COM installs its own routine, this
interrupt vector simply points to an IRET opcode.
-----------------------------------------------------------
* ;end of comment
;
;------------------------------------------------------------
; Updates for Version 3.0
;
; This version is fully compatible with IBM's PRINT command and
; hopefully most other print spoolers. I changed the method by which
; LPTx determines if a resident copy of itself is already in memory.
;
;------------------------------------------------------------
; This program intercepts the BIOS interrupt 17, the line printer
; interrupt. It will redirect the output of LPT1, LPT2, or LPT3 to a disk
; file. All three redirects may be active at the same time.
;
;
; Background:
;
; The basic problem with this type of program is that PC-DOS as written
; by Microsoft is not re-entrant. That means that if DOS is in control when
; the print interrupt occurs, you can not call DOS again to do some other
; function. Therefore, LPTx can not call DOS to write the captured print
; data to disk. Version 3.00 of LPTx tries to get around this by making
; PC-DOS re-entrant. Version 4.00 of LPTx gets around this by not ever
; trying to re-enter DOS.
;
;*******This Program Must be Converted to a .COM file before running ******
; Assemble with :
; masm lptx;
; link lptx;
; exe2bin lptx,lptx.com
; erase lptx.obj
; erase lptx.exe
;
;------------------------------------------------------------
;
; Operating Instructions
;
; Calling sequence:
; lptx -1 -o <d:[pathname]filename>
;
; where -1 means redirect LPT1, -2 means redirect LPT2, -3 means redirect
; LPT3
; This option must appear first
;
; -o means start the redirect to file speicfied. If rerouting
; is already in progress for the selected line printer,
; the old file will be closed first.
; (If you do not specify -o but you do specify a line printer,
; LPTx will use either the last file name that you gave when
; you loaded LPTx or will use the file named LPTX1.LST which it
; will create in the root directory
; on the default drive - where x is 1, 2, or 3.)
;
; It is not necessary that you specify the complete path name
; for the file. LPTx opens and closes the file each time that it
; writes out a block. If you change directories, LPTx will
; be able to find the file because it save the complete path.
;
; -c means close the file and send all furthur output directly to the
; line printer.
;
; if neither option is specified, LPTx just displays the program status.
;
; note: -1, -2, and -3 are mutually exclusive
; -o and -c are mutually exclusive
;
; examples:
;
; lptx Displays the program status and loads the
; resident portion of LPTx if it is not already
; loaded.
;
; lptx ? Displays a HELP screen
;
; lptx -1 routes LPT1 output to file named
; LPTX1.LST on the default drive or the last
; named file.
;
; lptx -o a:\able.xxx routes LPT1 output to file named
; or a:\able.xxx. Any open redirection
; lptx a:\able.xxx disk file for LPT1 is closed.
;
; lptx -2 b:xx.lst routes LPT2 output to file named
; XX.LST in the default directory
; on drive B:. Any open redirection
; disk file for LPT2 is closed.
;
; lptx -3 d:\ab\cd\file.lst redirects LPT3 output to the file named
; file.lst in the directory ab\cd on drive
; d:.
;
; lptx -c closes any disk files open for LPT1 and sends
; or the output back to the line printer
; lptx -1 -c If no rerouting is taking place to LPT1,
; this is a NOP. LPT2 and LPT3 are not
; affected.
;
; lptx -2 -c closes any disk file open for LPT2 and
; sends the output back to line printer.
; if no rerouting is taking place to LPT2,
; this is a NOP. LPT1 and LPT3 are not
; affected.
;
; By rerouting LPT2 or LPT3 to a disk file, you can in effect have 2 or 3
; printers on your system. LPT1 can be your physical printer and you can
; have LPT2 output going to disk. When you redirect LPT2 or LPT3, LPT1 works
; normally.
;
; If you are rerouting to a diskette file, do not remove the diskette
; once the rerouting starts. I recommend rerouting to a hard disk or
; a RAM disk.
;
; If LPTx encounters any kind of error during the rerouting, it terminates
; operation and sends output back to the line printer. It does not display
; anything but beeps the speaker four times. This prevents your currently
; running program from possibly getting destroyed.
; An error on LPT1 redirect does not shut down LPT2 or LPT3 redirect.
;
; LPTx captures the int 17h interrupt vector. It may not operate correctly
; with other routines what also intercept that vector.
;
; Problems may occur with print spoolers which also take over the int 17h
; vector. You can be sure that LPTx works correctly by running LPTx after
; you have run your print spooler. LPTx will be transparent to the print
; spooler but your print spooler may not be transparent to LPTx.
; LPTx works fine with IBM's PRINT command.
;
; LPTx also captures the int 24h critical error interrupt vector. This is
; done only for the period that LPTx is using the disk. This prevents
; the generation of funny error messages in the middle of other programs
; that you may be running. (LPTx justs beeps 4 times and clears itself
; out of way if a disk error occurs).
;
; LPTx can redirect all three printers to three different
; files with all 3 active at the same time.
;
; LPTx uses about 7K of memory for the resident data buffers and
; interrupt handler.
;
; If you modify or find any bugs in this program, I would appreciate
; it if you would drop me a line with the changes. Use the address
; above.
;
.list
if1
%out Pass 1 - Including Macros - v5.01
;
.xlist
;-----------------------------------------------------------------
;
; Macros
display macro msg
mov DX,offset msg
mov AH,DISPLAY_OUTPUT
int DOS_CALL
endm
;
.list
else
%out Pass 2
endif
;-----------------------------------------------------------------
NULL equ 0
OFF equ 0
ON equ 1
EMPTY equ 0
BEL equ 7
CR equ 13
LF equ 10
DOLLAR equ '$'
COLON equ ':'
BACKSLASH equ '\'
BLANK equ ' '
DASH equ '-'
DOS_CALL equ 21h
BUFSIZE equ 200h ;size of DMA buffer
DISPLAY_OUTPUT equ 9 ;for DOS call
DEF_DRIVE equ 19h
CREATE_FILE equ 3Ch
OPEN_FILE equ 3Dh
CLOSE_FILE equ 3Eh
WRITE_FILE equ 40h
DELETE_FILE equ 41h
LSEEK_FILE equ 42h
DEF_PATH equ 47h
FIND_FILE equ 4Eh
;-----------------------------------------------------------------
;
p_block struc
;
; data structure - these variables are used only in the
; memory resident copy of LPTx. BX is set to point to the offset of the
; allocation of this structure for the selected LPT
; NOTE : all of the labels in this structure are required as place
; holders even if not referenced. Used by the initialization calls
; later on.
;
active db OFF ;ON = this LPTx is on, OFF = off
handle dw NULL ;handle of disk file used by this LPT
filen db 'a:\lptx' ; space for redirection disk file name
pnum db BLANK
db '.lst',NULL
db ' '
db ' '
sp_left dw EMPTY ;bytes left in DMA buffer for this LPT
buffer db BUFSIZE dup(0FFh) ;data buffer for this LPT
;
p_block ends
;
;-----------------------------------------------------------------
;
subttl Main Code
page
%out Assembling CODE Segment
cseg segment para public 'CODE'
assume CS:cseg,DS:nothing,SS:nothing
org 100h
lptx: jmp l_start
;
; What follows is three allocations of the Structure p_block
; One for each line printer that we can support.
; With this, all three line printers have DMA buffers and flag
; variables.
; BX is used to point to the offset of the allocation currently in use
; Line printer 1
lpt1 p_block <,,,'1'>
; Line printer 2
lpt2 p_block <,,,'2'>
; Line printer 3
lpt3 p_block <,,,'3'>
;
lptxe db bel,bel,bel,bel,DOLLAR ;ring bell four times
;
crit_flag db OFF ;set to one if critical error occured
off_crit dw 0 ;save old critical error address
seg_crit dw 0
;
csect_off dw 0 ;pointer to Critical Section flag
csect_seg dw 0
prt_status db 10h ;printer status
P_NORMAL equ 10h ;Selected bit on only
P_TIMEOUT equ 01h ;Time out
save_psp dw 0 ;Save area for User's PSP Segment Address
lptx_psp dw 0 ;Our PSP Segment Address
byte_count dw 0 ;to save DOS byte count
stksav dd 0 ;caller's stack EA
db 128 dup('STACK ')
stk equ this byte
;-----------------------------------------------------------------
;
; Interrupt handler for interrupt 17h
;
int_17h proc far
; note : I changed this flag from version 4.00 so two versions
; of LPTx don't get intermixed.
cmp AH,3 ;AH=3 for LPTx Function
jne reg_call ;This is a regular print call
jmp ret_ack ;This is an LPTx Call
reg_call:
push BX
; if the caller is in the critical section of DOS, LPTx is idled
; temporarily and output goes to the printer
push DS ; check the critical section flag
push SI
lds SI,dword ptr CS:csect_off
cmp byte ptr [SI],OFF
pop SI
pop DS
jne sleep17 ;DOS in critical section
;this indicates that we cannot
;do any disk operations at this
;time
; set up BX to point to the data area for the requested printer
cmp DX,0 ;lpt1?
jne chk_lpt2 ;no
mov BX,offset lpt1 ;offset to LPT1
jmp short bx_set
chk_lpt2:
cmp DX,1 ;lpt2?
jne chk_lpt3 ;no
mov BX,offset lpt2 ;offset to LPT2
jmp short bx_set
chk_lpt3:
cmp DX,2 ;lpt3?
jne ill_ptr ;no - bad printer number
mov BX,offset lpt3 ;offset to LPT3
bx_set: cmp CS:[BX].active,OFF ;are we active?
je sleep17 ;no
mov CS:prt_status,P_NORMAL ;signal ready status
cmp AH,1 ;initialize call?
je do_nix ;yes
cmp AH,2 ;status call?
je do_nix ;yes
cmp AH,0 ;print call?
jne do_nix ;no
jmp prt_17 ;we are active
do_nix: mov AH,CS:prt_status ;return print status
rtn: pop BX
iret
ill_ptr:mov CS:prt_status,P_TIMEOUT ;time out status
jmp do_nix
ret_ack: ;return acknowledgement that I'm here
cmp DX,0ABCh ;my flag to detect that LPTx is
;already loaded and alive.
jne ret_nak ;return a NAK
mov DX,0BAAAh ;This is ack for Version 5.01
mov AX,0AAAAh
push CS ;now set up ES to point to the resident
pop ES ; data area
ret_nak:
iret ;return to interrupted program
sleep17:pop BX ;restore BX before we go to sleep
db 0EAh ;jump immediate to original handler
old_17h dd 0 ;address of old int 17h routine
prt_17: push AX ; Start the print process.
push BX ; Character is in AL.
push CX
push DX
push DS
push ES
push SI
push DI
push BP
push CS ; DS is used as the segment register
pop DS ; for all data during the interrupt
cli
mov SI,SS
mov word ptr DS:stksav+2,SI ;save caller's stack pointer
mov SI,SP
mov word ptr DS:stksav,SI
mov SI,CS
mov SS,SI ;give me new bigger stack
mov SI,offset stk
mov SP,SI
sti
mov DS:prt_status,P_NORMAL ;signal ready status
;prt_status is set before
;the call to prnt so that prnt
;can change it if the print to
;disk fails
call prnt ;print the character
cli
mov SI,word ptr DS:stksav
mov SP,SI ;restore caller's stack pointer
mov SI,word ptr DS:stksav+2
mov SS,SI
sti
pop BP
pop DI
pop SI
pop ES
pop DS
pop DX
pop CX
pop BX
pop AX
jmp do_nix
int_17h endp
;-----------------------------------------------------------------
;
; Interrupt handler for interrupt 21h
;
int_21h proc far
push BX
cmp AH,5 ;is this a DOS printer call?
je int_21h_5 ;yes
cmp AH,40h ;is this a DOS write call?
je int_21h_40 ;yes
jmp sleep21 ;no - go on to real DOS
; DOS Function 40h - Write to File or Device. DS:DX contains the address
;of data to write. CX contains the byte count. Return AX = byte count.
int_21h_40:
cmp BX,0004 ;Standard Printer?
jne sleep21 ;no - go on to read DOS
; if the caller is in the critical section of DOS, LPTx is idled
; temporarily and output goes to the printer
push DS ; check the critical section flag
push SI
lds SI,dword ptr CS:csect_off
cmp byte ptr [SI],OFF
pop SI
pop DS
jne sleep21 ;DOS in critical section
;this indicates that we cannot
;do any disk operations at this
;time
; set up BX to point to the data area for the requested printer
; Since we don't know what the standard printer device is, we
; use LPT1
mov BX,offset lpt1 ;offset to LPT1
cmp CS:[BX].active,OFF ;are we active?
je sleep21 ;no
push AX ;now do it
push BX
push CX
push DX
mov byte_count,CX ;save byte count
mov BX,DX
;DS:BX points to buffer, CX has byte count
cmp CX,0 ;check for zero byte count
je prt_21_done
loop21_40:
mov AL,[BX] ;get a character
mov AH,0 ;set up for call to interrupt 17h
int 17h ;note : int17h returns a printer status
;in AH but DOS does not define a way
inc DX
loop loop21_40
prt_21_done:
pop DX
pop CX
pop BX
pop AX
mov AX,byte_count ;DOS returns byte count
jmp exit21 ;to return it to the user.
;
int_21h_5:
; DOS Function 5 Printer Output. The Character in DL is output to the
; standard printer device.
; if the caller is in the critical section of DOS, LPTx is idled
; temporarily and output goes to the printer
push DS ; check the critical section flag
push SI
lds SI,dword ptr CS:csect_off
cmp byte ptr [SI],OFF
pop SI
pop DS
jne sleep21 ;DOS in critical section
;this indicates that we cannot
;do any disk operations at this
;time
; set up BX to point to the data area for the requested printer
; Since we don't know what the standard printer device is, we
; use LPT1
mov BX,offset lpt1 ;offset to LPT1
cmp CS:[BX].active,OFF ;are we active?
je sleep21 ;no
push AX
mov AH,0 ;set up for call to interrupt 17h
mov AL,DL ;the character
int 17h
pop AX
;note : int17h returns a printer status
;in AH but DOS does not define a way
;to return it to the user.
exit21: pop BX
popf ;restore flags
clc ;never an error from us
ret ;return to caller (regular FAR return)
;
sleep21:pop BX ;restore BX before we go to sleep
db 0EAh ;jump immediate to original handler
old_21h dd 0 ;address of old int 21h routine
int_21h endp
;-----------------------------------------------------------------
;
; Critical Error Handler
;
crit_int proc far ;got critical error
mov CS:crit_flag,ON ; set flag
mov AL,0 ;tells DOS to ignore the
iret ;error
crit_int endp
;-----------------------------------------------------------------
;
; PRNT - Print a character in AL
;
prnt proc near
push DS
cmp DS:[BX].active,OFF
je prtext ;nothing there?
push AX
cmp DS:[BX].sp_left,BUFSIZE ;buffer full
jne intadd ;no
call flush ;flush the buffer to disk
;
intadd: pop AX
mov DI,BX ;offset of this printer's allocation
add DI,offset buffer ;add in offset of buffer
add DI,DS:[BX].sp_left ;add in current byte count
mov DS:[DI],AL ;stuff it
inc DS:[BX].sp_left
prtext: pop DS
ret ;done
prnt endp
;------------------------------------------------------------
;
; FLUSH - Flush print buffer to disk file
;
flush proc near
cmp DS:[BX].sp_left,EMPTY ;buffer empty?
jne flush_buf ;no, write it to disk
ret ;exit
flush_buf:
call disk_out
ret
flush endp
;------------------------------------------------------------
;
; DISK_OUT - write to disk
;
disk_out proc near
mov DS:[BX].sp_left,EMPTY ;set buffer empty
;PSP
push BX
mov AH,51h ;get current PSP
int DOS_CALL
mov DS:save_psp,BX ;and save it
mov BX,DS:lptx_psp ;get our PSP
mov AH,50h
int DOS_CALL ;set it into DOS
pop BX
;
push ES
push DS
mov AX,DS ;set up ES
mov ES,AX
push BX
push ES
mov AX,3524h ;get old critical error vector
int DOS_CALL
mov DS:off_crit,BX
mov DS:seg_crit,ES
mov DX,offset crit_int
mov AX,2524h
int DOS_CALL ;trap critical error vector
mov DS:crit_flag,OFF ;clear critical error flag
pop ES
pop BX
mov DX,BX ;open file
add DX,offset filen ;filename
mov AL,1 ;open for writing
mov AH,open_FILE
int DOS_CALL
mov DS:[BX].handle,AX ;file handle
jc disk_err ;error
cmp DS:crit_flag,ON ;critical error?
je disk_err ;yes
push BX
mov AH,lseek_FILE
mov AL,2 ;end of file
mov CX,0 ;offset 0
mov DX,0
mov BX,DS:[BX].handle
int DOS_CALL
pop BX
jc disk_err ;some seek error
cmp DS:crit_flag,ON ;critical error?
je disk_err ;yes
mov CX,BUFSIZE ;buffer length
mov DX,BX ;offset of structure allocation
add DX,offset buffer ;add offset of buffer within the
; allocation
push BX
mov AH,write_FILE
mov BX,DS:[BX].handle ;file handle
int DOS_CALL ;buffer address is DS:DX
pop BX
jnc disk_ok
cmp DS:crit_flag,ON ;critical error?
je disk_err ;yes
cmp AX,BUFSIZE ;did DOS write it all?
je disk_ok ;yes
disk_err:
display lptxe ;ring bell
mov DS:[BX].active,OFF ;turn us off
mov DS:crit_flag,OFF ;clear error flag
mov DS:prt_status,P_TIMEOUT ;signal time out error
;then try to close the file
;to save what we can
disk_ok:
push BX
mov BX,DS:[BX].handle
mov AH,close_FILE ;close the file
int DOS_CALL
pop BX
disk_exit:
pop DS
pop ES
push DS
lds DX,dword ptr DS:off_crit
mov AX,2524h ;restore critical error vector
int DOS_CALL
pop DS
; PSP
push BX
mov BX,DS:save_psp ;get user's PSP
mov AH,50h
int DOS_CALL ;set it back into DOS
pop BX
;
ret
disk_out endp
;
end_res db 0
;
; This is the end of the memory resident portion of LPTx
;
;--------------------------------------------------------------------
;
; All of the following data is in the Code Segment
;
mach_type db 0
DOS_version db 0 ;Major Version Number
db 0 ;Minor Version Number
drive db 0 ;default drive number 0=A etc.
flag_27 db OFF ; 1=make this copy resident
wrong_dos db 'DOS 2.0 or later required for LPTx',LF,CR,DOLLAR
up_msg db 'LPTx - Line Printer Redirection Program - V5.01'
db LF,CR,' Copyright 1985 Mark C. DiVecchio',LF,CR
db DOLLAR
resident db LF,CR,'Resident Portion of LPTx Loaded',LF,LF,CR
db DOLLAR
lptx_err_3 db 'Could not delete file',LF,CR,DOLLAR
lptx_over db CR,LF,'File already exists. Do you want to overwrite '
db 'it? (y or n) :',DOLLAR
lptx_nc db 'File selection canceled',CR,LF,DOLLAR
lptx_del db 'File is being overwritten',LF,CR,DOLLAR
lptx_cr db LF,CR,DOLLAR
lptx_bad db 'Invalid Option',LF,CR
db 'Calling sequence:',LF,CR
db 'lptx {-1,-2,-3} {-c -o <d:[pathname]filename>}'
db LF,CR,DOLLAR
lptx_on db LF,CR,'Redirection started. Disk file opened.'
db LF,CR,DOLLAR
lptx_off db LF,CR,'Redirection ended. Disk file closed.'
db LF,CR,DOLLAR
lptx_creat db 'Could not create the disk file',LF,CR,DOLLAR
; HELP screen
help_msg db LF,CR,'Calling sequence : ',LF,LF,CR
db 'LPTx -p -f <[d:][\pathname\pathname]filename>'
db LF,LF,CR
db ' where p = printer number : 1, 2, or 3',LF,CR
db ' f = function : o for open a print file'
db LF,CR
db ' c for close a print file'
db LF,CR
db ' drive letter & pathname are optional'
db LF,CR
db ' defaults : p = 1',LF,CR
db ' f = o',LF,CR,DOLLAR
;
stat_stat db CR,LF,'LPTx Status :',CR,LF,DOLLAR
stat_lp db 'lpt'
stat_ptr db ' : ',DOLLAR
stat_off db ' not redirected',CR,LF,DOLLAR
stat_dir db ' redirected to disk file '
stat_fn db 60 dup (BLANK)
;
yn_max db 2 ;max # of char
yn_act db 0
yn_in db 2 dup (0)
;
;--------------------------------------------------------------------
;
; This is the main routine which is executed each time that LPTx is
; called. In this routine, DS points to the data segment which is
; transient. ES points to the data segment which is permanently
; resident. ES:BX points to the data structure for the selected
; line printer, 1, 2, or 3.
; The offsets are the same for both. If this is the first
; time that LPTx is run, then ES=DS.
;
l_start:sti ;interrupts on
push DS ;Save DS
xor AX,AX ;clear AX for return IP
push AX ;put 0 on stack
;
;to check for machine type look at
; F000:FFFE ; I don't use this currently
; = FF IBM PC
; = FE IBM XT or Portable
; = FD IBM PCjr
; = FC IBM PC AT
; = F9 IBM Convertible
mov AX,0F000h
mov ES,AX
mov BX,0FFFEh
mov CL,ES:[BX] ;get machine type
mov mach_type,CL ;save machine type
;
;PSP
mov lptx_psp,DS ;put away our PSP Segment address
;
; get the DOS version number
; returns zero for pre DOS 2.0 releases
mov AH,30h
int DOS_CALL ;call DOS
mov word ptr DOS_version,AX
; Requires at least version 2.0 and may or may not work
; with versions above 2.1
cmp DOS_version,2 ;is it DOS 2.+
jge dos_ok ;yes
display wrong_dos ;print error message
mov AH,0
int DOS_CALL ;terminate
dos_ok: mov AH,def_drive ;get current default drive
int DOS_CALL
mov drive,AL ;save the drive number
display up_msg ;print program ID
mov flag_27,OFF ;to not make resident
; are we already resident in memory?
mov DX,0ABCh ;check if LPTx is already resident
mov AH,3 ;get status - special call
int 17h ;call int 17h - BIOS
cmp DX,0BAAAh ;my handler sets DX to 0BAAAh
;and sets ES
je in_core ;yes - ES has segment address
mov flag_27,ON ;to make this copy resident
push CS
pop ES ;set ES to CS for segment address
mov AL,drive
add AL,'a' ;make it a letter
mov BX,offset lpt1
mov ES:[BX].filen,AL ;put it into the filename
mov BX,offset lpt2
mov ES:[BX].filen,AL ;put it into the filename
mov BX,offset lpt3
mov ES:[BX].filen,AL ;put it into the filename
; ----------------------------------------------------
in_core: ;ES is ok
; ES now points to resident data area
; set up ES:BX to point to default data structure
mov BX,offset lpt1 ;offset - default to LPT1
;get options and file name
;scan input line for line printer number
mov SI,81h ;starting offset
mov CL,DS:80h ;length of input line
mov CH,0
cmp CX,0 ;nothing?
jne inp_lp ;no
jmp make_res ;yes, then just make LPTx resident
;if it isn't already
inp_lp:
cmp byte ptr DS:[SI],'?' ;a ? ?
jne cont_scan ;no
jmp help ;yes - go show help data
cont_scan:
cmp byte ptr DS:[SI],dash ;a dash ?
je got_opt ;yes
cmp byte ptr DS:[SI],CR ;a carriage return?
je scan_done ;yes
cmp byte ptr DS:[SI],BLANK ;a blank?
je inp_ret ;yes
jmp no_b ;assume that we got a file name
;without the -o option
inp_ret:
inc SI ;ignore blanks
loop inp_lp ;continue to scan
; Scan of whole line is complete, if options were not found, we
; use defaults : LPT1 and file LPTX1.LST on the default drive.
; NOTE : at least one option must be specified.
scan_done:
jmp lptx_make ;go create the file
got_opt: ;we got an option
inc SI ;to option
cmp byte ptr DS:[SI],'1' ;LPT1?
jne chk_2
mov BX,offset lpt1 ;offset from ES
jmp short inp_ret
chk_2: cmp byte ptr DS:[SI],'2' ;LPT2?
jne chk_3
mov BX,offset lpt2 ;offset from ES
jmp short inp_ret
chk_3: cmp byte ptr DS:[SI],'3' ;LPT3?
jne chk_fil
mov BX,offset lpt3 ;offset from ES
jmp short inp_ret
chk_fil: ;is it file?
cmp byte ptr DS:[SI],'o' ;open a file
je file_op ;yes
cmp byte ptr DS:[SI],'c' ;close a file
je file_cl ;yes
display lptx_bad ;incorrect option
jmp nor_ex
file_cl: ;close the output file
cmp ES:[BX].active,ON ;are we active?
jne no_close ;no
mov AL,1AH ;CTRL-Z
push DS
push ES
pop DS ;set DS to point to resident
;data segment
call prnt ;print end of file mark
call flush ;flush out write buffer
pop DS ;restore DS
mov ES:[BX].active,OFF ;make us inactive
display lptx_off ;redirection off message
no_close:
jmp nor_exit ;nothing to close so exit
file_op: ;open a file for output
;get the file name
inc SI ;to next chracter
cmp byte ptr DS:[SI],BLANK ;a blank?
jne no_b ;no
inc SI ;skip over blank
no_b:
; at this point, we have found a new file name. We close the old
; file if one was open
cmp ES:[BX].active,ON ;are we active?
jne no_cl ;no
mov AL,1AH ;CTRL-Z
push DS
push ES
pop DS ;set DS to point to resident
;data segment
call prnt ;print end of file mark
call flush ;flush out write buffer
pop DS ;restore DS
mov ES:[BX].active,OFF ;make us inactive
display lptx_off ;redirection off message
no_cl: mov DI,BX ;base of structure
add DI,offset filen ;add offset of destination
push SI ;save pointer to file name
; search for a drive letter
inc SI ;should point to a colon if
;one is there
cmp byte ptr [SI],COLON ;?
je got_drive ;yes
get_drive:
mov AL,drive ;get drive letter
add AL,'a' ;make it a letter
mov ES:[DI],AL ;put it in file name
inc DI
mov byte ptr ES:[DI],COLON ;put in a colon
inc DI
jmp path_search
got_drive:
pop SI ;move pointer back to start
mov AL,[SI] ;get the given drive
mov ES:[DI],AL ;move it
sub AL,'a' ;make it a number
mov drive,AL ;save the drive number
inc SI
inc DI
mov byte ptr ES:[DI],COLON
inc DI
inc SI
push SI ;save new start pointer
path_search:
; now search for a backslash which says that a pathname was given
bk_s_lp:cmp byte ptr [SI],BACKSLASH
je got_path ;a path
cmp byte ptr [SI],CR ;end of the file name?
je get_path ;yes with no path
inc SI
jmp short bk_s_lp ;loop
get_path:
mov byte ptr ES:[DI],BACKSLASH ;create the path
inc DI
mov DL,drive ;the current drive
inc DL ;bump it for DOS
push DS
push ES
pop DS ;set up DS for DOS
mov SI,DI ;set up SI for pathname
mov AH,def_path ;get current directory
int DOS_CALL ;path goes into DS:SI
pop DS ;restore DS
cmp byte ptr ES:[SI],NULL ;null path?
je null_path ;yes - root directory
path_lp: ;now find the end of the string
cmp byte ptr ES:[SI],NULL ;null byte marks end of pathname
je end_path ;now append the file name
inc SI
jmp short path_lp
end_path:
mov byte ptr ES:[SI],BACKSLASH
inc SI
null_path:
mov DI,SI ;DI is destination
got_path:
pop SI ;restore source of filename
; pick up everything to next blank
get_lp:
mov AL,DS:[SI] ;character
mov ES:[DI],AL ;put it away
cmp AL,CR ;was it a Carriage Return?
je end_line
cmp AL,BLANK ;was it a space?
je end_line
inc SI
inc DI
jmp short get_lp ;no so get next character
end_line:
mov byte ptr ES:[DI],NULL ;zero out the CR or blank
;at the end of the filename
;it becomes an ASCIIZ string
sub DI,BX ;now take out the base and
cmp DI,offset filen ; make sure that we got something
jne lptx_make ;file name was ok
display lptx_creat ;could not understand the file name
jmp nor_exit ;don't stay resident
nor_ex: jmp nor_exit
lptx_make:
; default DTA used by Find File is set by DOS to an offset of
; 80h into this program's Program Segment Prefix
push DS
push ES
pop DS ;uses DS:DX
mov DX,BX
add DX,offset filen ;file name
mov AH,find_FILE
mov CX,0 ;normal files only
int DOS_CALL ;find first match
pop DS
jnc lptx_d ;file was found
jmp lptx_create ;not there - which is ok
;file already exists
lptx_d: display lptx_over
mov DX,offset yn_max;input buffer
mov AH,0AH
int DOS_CALL
cmp yn_act,0 ;anything typed?
display lptx_cr
je lptx_x ;no - exit
cmp yn_in,'y' ;a yes?
je lptx_d_yes ;yes
cmp yn_in,'Y' ;a yes?
je lptx_d_yes ;yes
lptx_x: display lptx_nc
jmp nor_exit ;all done if we can't overwrite
;see if we should abort the host
lptx_d_yes:
display lptx_del
push DS
push ES
pop DS ;uses DS:DX
mov DX,BX
add DX,offset filen ;file name
mov AH,delete_FILE
int DOS_CALL ;delete file
pop DS
jnc lptx_create ;ok its gone
display lptx_err_3 ;can't delete it
jmp nor_exit
lptx_create: ; create the file
push DS
push ES
pop DS ;uses DS:DX
mov DX,BX ;base of this LPT's structure
add DX,offset filen ;file name
mov AH,create_FILE
mov CX,0 ;normal files only
int DOS_CALL ;find first match
pop DS
jnc creat_ok
display lptx_creat ;could not create the file
jmp nor_exit ;don't stay resident
creat_ok: ;now close the file
push BX
mov BX,AX ;AX was loaded by the create file
; call
mov AH,close_FILE ;close the file
int DOS_CALL
pop BX
display lptx_on
; set the program up for writing
mov ES:[BX].sp_left,EMPTY ;set buffer empty
mov ES:[BX].active,ON ;set us on
make_res:
cmp flag_27,ON ;make this one resident?
jne nor_exit ;no
push ES
push BX
; get old interrupt handler addressses
mov AL,17h ;get current vector address for 17h
mov AH,35h
int DOS_CALL
mov word ptr old_17h,BX
mov word ptr old_17h[2],ES ;save it for later use
mov AL,DOS_CALL ;get current vector address for 21h
mov AH,35h
int DOS_CALL
mov word ptr old_21h,BX
mov word ptr old_21h[2],ES ;save it for later use
;
; Set LPTx up as the new int 17h interrupt handler
mov AX,2517h ;set interrupt vector
mov DX,offset int_17h ;BIOS printer
int DOS_CALL
;
; Set LPTx up as the new int 21h interrupt handler
mov AX,2521h ;set interrupt vector
mov DX,offset int_21h ;DOS Functions
int DOS_CALL
;
mov AH,34h
int DOS_CALL ;Call Special DOS interrupt
;returns pointer to critical
;section flag in ES:BX
;With DOS 2.1, this returns
;00EC:012D. I used the XRAY
;program to look at this
;byte while DOS was running.
mov csect_seg,ES ;save the pointer
mov csect_off,BX
pop BX
pop ES
display resident ;resident loaded message
call stat ;display status
mov DX,offset end_res
int 27h ;terminate but stay resident
; HELP printer
help: display help_msg ;display the HELP screen
jmp short nor_exit
; Normal exit for transient copy of LPTx
nor_exit:
call stat ;display status
bail_out:
mov AH,0
int DOS_CALL ;terminate
;------------------------------------------------------------------------
;
; displays the status of each of the three line printers
;
stat proc near
; display each LPTx with a message "not redirected"
; or redirected to <filename>
push AX
push BX
push DX
push SI
push DI
display stat_stat
stat_1: mov BX,offset lpt1 ;first printer
mov stat_ptr,'1'
display stat_lp
cmp ES:[BX].active,ON ;are we active?
je stat_1_a ;yes
display stat_off
jmp short stat_2
stat_1_a:
mov SI,BX ;base
add SI,offset filen ;offset
mov DI,offset stat_fn
stat_1_lp:
mov AL,ES:[SI]
mov [DI],AL
inc SI
inc DI
cmp AL,NULL ;loop till a null byte is found
jne stat_1_lp
mov byte ptr [DI],CR
inc DI
mov byte ptr [DI],LF
inc DI
mov byte ptr [DI],DOLLAR
display stat_dir ;display file name
stat_2:
mov BX,offset lpt2 ;second printer
mov stat_ptr,'2'
display stat_lp
cmp ES:[BX].active,ON ;are we active?
je stat_2_a ;yes
display stat_off
jmp short stat_3
stat_2_a:
mov SI,BX ;base
add SI,offset filen ;offset
mov DI,offset stat_fn
stat_2_lp:
mov AL,ES:[SI]
mov [DI],AL
inc SI
inc DI
cmp AL,NULL ;loop till a null byte is found
jne stat_2_lp
mov byte ptr [DI],CR
inc DI
mov byte ptr [DI],LF
inc DI
mov byte ptr [DI],DOLLAR
display stat_dir ;display file name
stat_3: mov BX,offset lpt3 ;third printer
mov stat_ptr,'3'
display stat_lp
cmp ES:[BX].active,ON ;are we active?
je stat_3_a ;yes
display stat_off
jmp short stat_done
stat_3_a:
mov SI,BX ;base
add SI,offset filen ;offset
mov DI,offset stat_fn
stat_3_lp:
mov AL,ES:[SI]
mov [DI],AL
inc SI
inc DI
cmp AL,NULL ;loop till a null byte is found
jne stat_3_lp
mov byte ptr [DI],CR
inc DI
mov byte ptr [DI],LF
inc DI
mov byte ptr [DI],DOLLAR
display stat_dir ;display file name
stat_done:
pop DI
pop SI
pop DX
pop BX
pop AX
ret
stat endp
;
cseg ends
%out EOF
end lptx